package dataStructs;

import java.util.ArrayList;
import java.util.Iterator;


public class HashTable<K extends Comparable<? super K>, V> implements Iterator<Node<K, V>>{

	private int size;
	private int numberOfElements;
	private ArrayList<Node<K, V>>[] buckets;
	private int current; //for iterator
	private int currentBucket; //for iterator
	private int currentBucketPosition; //for iterator


	public HashTable(int size) {
		super();
		this.size = size;
		this.initialize();
	}
	
	public HashTable(int size, ArrayList<Node<K, V>> nodes) {
		super();
		this.size = size;
		this.initialize();
		this.addAll(nodes);
	}

	public HashTable() {
		super();
		this.size = 701;
		this.initialize();
	}
	
	public HashTable(ArrayList<Node<K, V>> nodes) {
		super();
		this.size = 701;
		this.initialize();
		this.addAll(nodes);
	}

	@SuppressWarnings("unchecked")
	private void initialize() {
		this.numberOfElements = 0;
		setDefaultIterator();
		buckets = new ArrayList[this.size];
	}

	public int getSize() {
		return size;
	}

	public ArrayList<Node<K, V>> getAll() {
		
		ArrayList<Node<K, V>> nodes = new ArrayList<Node<K, V>>();
		
		for (int i = 0; i < buckets.length; i++) {
			if (buckets[i] != null)
				nodes.addAll(buckets[i]);
		}
		return nodes;
	}

	public int[] getAllCodes() {
		int[] codes = new int[this.numberOfElements];
		int k = 0;
		for (int i = 0; i < buckets.length; i++) {
			if (buckets[i] != null)
				for (int j = 0; j < buckets[i].size(); j++) {
					codes[k] = buckets[i].get(j).hashCode();
					k++;
				}
		}
		return codes;
	}

	private int hashCode(K k) {
		int pos = Math.abs(k.hashCode()) % size;
		
		return reHashing(k, pos);
	}
	
	private int reHashing(K k, int pos){
		
		int counter = 0;

		while ((counter < size) && (this.buckets[pos] != null) && (this.buckets[pos].get(0).getKey().compareTo(k) != 0)){
			counter++;
			pos = (pos + 1) % size;
		}
		
		return (counter == this.size ? -1 : pos); //-1 erro
	}
	
	public void addAll(ArrayList<Node<K, V>> nodes) {
		
		for (Node<K, V> node: nodes) {
			this.add(node);
		}
	}
	
	
	public void add(Node<K, V> node) {
		

		//if (this.get(node.getKey()) == null) {
		
			int pos = this.hashCode(node.getKey());
			
			try {
				buckets[pos].add(node);
				
			} catch (NullPointerException e) {
				
				buckets[pos] = new ArrayList<Node<K, V>>();
				buckets[pos].add(node);
			}finally{	
				this.numberOfElements++;
			}
		
	}

	public V get(K k) {
		
		int pos = this.hashCode(k);
		
		if (buckets[pos] == null)
			return null;
				
		for (int i = 0; i < buckets[pos].size(); i++) {
						
			if (buckets[pos].get(i).getKey().equals(k))
				return buckets[pos].get(i).getValue();
		}
		return null;
	}
	
	public ArrayList<Node<K, V>> getCollision(K k) {
		
		int pos = this.hashCode(k);
		
		if (buckets[pos] == null)
			return null;
		
		return buckets[pos];
	}

	public void remove(K k) {
		V v = this.get(k);
		int pos = this.hashCode(k);
		this.buckets[pos].remove(v);
	}

	@Override
	public boolean hasNext() {
		// TODO Auto-generated method stub
		return (this.current < (this.numberOfElements-1));
	}

	@Override
	public Node<K, V> next() {
		// TODO Auto-generated method stub
		Node<K, V> node = null;
		
		current++;
		currentBucketPosition++;
		
		try {
			node = this.buckets[this.currentBucket].get(currentBucketPosition);
		}
		catch(Exception e){
			
			do {
				this.currentBucket++;
			} while ((this.currentBucket < buckets.length) && (this.buckets[this.currentBucket] == null));

			this.currentBucketPosition = 0;
			node = this.buckets[this.currentBucket].get(this.currentBucketPosition);
			
			return node;
		}
		
		return node;
	}

	@Override
	public void remove() {
		// TODO Auto-generated method stub
		
	}
	
	public void setDefaultIterator() {
		this.current = -1;
		this.currentBucket = 0;
		this.currentBucketPosition = -1;		
	}

}
